#include <thread>
#include "Chapter6.hpp"
#include "UniversalRef.hpp"
#include <chrono>
#include <iostream>
#include <iomanip>
#include <memory>
#include <atomic>
#include <assert.h>

std::string time_to_string_with_us()
{
    auto now = std::chrono::system_clock::now();
    auto us = std::chrono::duration_cast<std::chrono::microseconds>(
                  now.time_since_epoch()) %
              1000000;

    std::time_t t = std::chrono::system_clock::to_time_t(now);
    std::tm tm = *std::localtime(&t);

    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S")
        << "." << std::setfill('0') << std::setw(6) << us.count();
    return oss.str();
}

typedef threadsafe_queue<int> ts_queue;
typedef threadsafe_lookup_table<int, int> ts_table;
typedef threadsafe_list<int> ts_list;

void test_pop_data(ts_queue &queue)
{
    auto now = std::chrono::system_clock::now();
    auto timeout = now + std::chrono::seconds(15);
    while (now < timeout)
    {
        int temp{0};
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        if (queue.try_pop(temp))
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << " Get poped value:" << temp << "\n";
        else
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << " queue might be empty right now" << "\n";
        // sleep 1s
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        now = std::chrono::system_clock::now();
    }
}

void test_push_data(ts_queue &queue)
{

    for (int i = 1; i <= 15; ++i)
    {
        std::time_t now_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
        std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << " Push value:" << i << "\n";
        queue.push(i);
        if (i % 2)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::seconds(2));
        }
    }
}

void test_wait_pop(ts_queue &queue)
{
    auto now = std::chrono::system_clock::now();
    auto timeout = now + std::chrono::seconds(15);
    while (now < timeout)
    {
        int temp{0};
        queue.wait_and_pop(temp);
        now = std::chrono::system_clock::now();
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        if (temp)
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << " Get poped value:" << temp << "\n";
        else
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << " queue might be empty right now" << "\n";
        // sleep 1s
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        now = std::chrono::system_clock::now();
    }
}

void test_add_or_update(ts_table &table, int key)
{
    auto now = std::chrono::system_clock::now();
    auto timeout = now + std::chrono::seconds(15);

    int temp{0};
    while (now < timeout)
    {
        int val = table.value_for(key, temp);
        now = std::chrono::system_clock::now();
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        if (val == temp)
        {
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << "Thread one, Cannot find key \n";
        }
        else
        {
            std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << "Thread one, Find key, updatint value to " << temp << "\n";
        }

        table.add_or_update_mapping(key, temp++);
        std::this_thread::sleep_for(std::chrono::seconds(1));
        now = std::chrono::system_clock::now();
    }
}

void test_remove(ts_table &table, int key)
{
    auto now = std::chrono::system_clock::now();
    auto timeout = now + std::chrono::seconds(15);
    while (now < timeout)
    {
        table.remove_mapping(key);
        now = std::chrono::system_clock::now();
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        std::cout << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << "Thread two, Removed key." << "\n";
        // sleep 0.5s
        std::this_thread::sleep_for(std::chrono::seconds(3));
        now = std::chrono::system_clock::now();
    }
}

bool remover(int a)
{
    return a % 2;
}

void printer(int a)
{
    std::cout << a << ",";
}

// 先往里面塞10条数据，等待5秒，再往里面塞20条数据
void test_push_front(ts_list &list)
{
    // auto now = std::chrono::system_clock::now();
    // auto timeout = now + std::chrono::seconds(15);
    for (int i = 0; i < 500000; ++i)
    {
        list.push_front(i);
        // std::cout << time_to_string_with_us() << "Thread one, Added key " << i << " ." << "\n";
        // sleep 0.1s
        // std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    // std::this_thread::sleep_for(std::chrono::seconds(5));

    // std::this_thread::sleep_for(std::chrono::milliseconds(500));
    for (int i = 0; i < 20; ++i)
    {
        std::cout << time_to_string_with_us() << "Thread one, started \n";
        list.push_front(i);
        std::cout << time_to_string_with_us() << "Thread one, Added key " << i << " ." << "\n";
        // sleep 0.5s
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

void test_delete(ts_list &list)
{

    std::this_thread::sleep_for(std::chrono::seconds(2));
    auto now = std::chrono::system_clock::now();
    auto timeout = now + std::chrono::seconds(10);
    while (now < timeout)
    {
        std::cout << time_to_string_with_us() << "Thread two, started." << "\n";
        list.remove_if(remover);
        std::cout << time_to_string_with_us() << "Thread two, Removed odd key." << "\n";
        // list.for_each(printer);
        // std::cout << "\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        now = std::chrono::system_clock::now();
    }
}

// int main(int argc, char **argv)
// {
//     ts_queue a;
//     // 20250604 简单测试ts_queue
//     std::thread one(test_push_data, std::ref(a));
//     std::thread two(test_wait_pop, std::ref(a));
//     // std::thread two(test_pop_data, std::ref(a));
//     return 0;
// }

// int main(int argc, char **argv)
// {
//     ts_table b;
//     // 20250604 简单测试ts_table
//     int key = 99;
//     std::thread one(test_add_or_update, std::ref(b), key);
//     std::thread two(test_remove, std::ref(b), key);
//     return 0;
// }

// int main(int argc, char **argv)
// {
//     ts_list c; // 20250608 简单测试ts_list
//     std::thread one(test_push_front, std::ref(c));
//     std::thread two(test_delete, std::ref(c));
//     one.join();
//     two.join();
//     return 0;
// }

// int main(int argc, char **argv)
// {
//     // test template, auto and universal reference.
//     int x{27};
//     const int y{28};
//     const int & z{x};
//     int & k{x};
//     f1(x);
//     // f1(26); //  error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int
//     f1(y);
//     f1(z);
//     f1(k);

//     f2(x);
//     f2(26); 
//     f2(y);
//     f2(z);
//     f2(k);

//     f3(x);
//     f3(26); 
//     f3(y);
//     f3(z);
//     f3(k);
//     std:: cout << "-------------------------\n";
//     // test template, auto and universal reference.
//     std::string  a{"test"};
//     const std::string b{"const test"};
//     const std::string & c{a};
//     std::string & d{a};
//     f1(a); //void f1<std::string>(std::string &param)
//     f1("rvalue test"); //void f1<const char [12]>(const char (&param)[12]) 
//     f1(b); //void f1<const std::string>(const std::string &param)
//     f1(c); //void f1<const std::string>(const std::string &param)
//     f1(d); //void f1<std::string>(std::string &param)
//     std::string aaa{a};
//     // f1(std::move(aaa)); // error: cannot bind non-const lvalue reference of type ‘std::__cxx11::basic_string<char>&’ to an rvalue of type ‘std::remove_reference<std::__cxx11::basic_string<char>&>::type’ {aka ‘std::__cxx11::basic_string<char>’}

//     f2(a); //void f2<std::string &>(std::string &param)

//     f2("rvalue test"); //void f2<const char (&)[12]>(const char (&param)[12])

//     f2(b); //void f2<const std::string &>(const std::string &param)

//     f2(c); //void f2<const std::string &>(const std::string &param)

//     f2(d); //void f2<std::string &>(std::string &param)

//     std::string aa{a};
//     f2(std::move(aa)); //void f2<std::string>(std::string &&param)


//     f3(a); //void f3<std::string>(std::string param)

//     f3("rvalue test"); //void f3<const char *>(const char *param)

//     f3(b); //void f3<std::string>(std::string param)

//     f3(c); //void f3<std::string>(std::string param)

//     f3(d); //void f3<std::string>(std::string param)

//     std::string aaaa{a};
//     f3(std::move(aaaa)); //void f3<std::string>(std::string param)


//     std::string bbb{"test universal reference."};
//     std::cout << bbb << ", address is " << &bbb << "\n";
//     char temp_bbb('b');
//     testUniversalRef(bbb);
//     char temp_aaaa('a');
//     testUniversalRef(std::move(bbb));

//     std::cout << bbb << ", address is " << &bbb << "\n";

//     std::string ccc{"test universal reference on unique ptr."};
//     std::unique_ptr<std::string> cc = std::make_unique<std::string>(std::move(ccc));

//     std::cout << cc.get() << ", content is " << *cc << "\n";
//     // testUniversalRefOfUptr(cc); // unique_ptr(const unique_ptr&) = delete;

//     char temp_cc('a');
//     // std::cout << &temp_cc << "\n";
//     testUniversalRefOfUptr(std::move(cc));

//     char temp_dd('a');
//     // std::cout << &temp_dd << "\n";
//     if (cc == nullptr) {
//         std::cout << "cc is nullptr now. \n";
//     }else {
//         // std::cout << cc.get() << ", content is been moved. \n" ;  
//     }
// }

std::atomic<int> data[5];
std::atomic<bool> sync1(false),sync2(false);

void thread_1()
{
    data[0].store(42,std::memory_order_relaxed);
    data[1].store(97,std::memory_order_relaxed);
    data[2].store(17,std::memory_order_relaxed);
    data[3].store(-141,std::memory_order_relaxed);
    data[4].store(2003,std::memory_order_relaxed);
    sync1.store(true,std::memory_order_release);
}

void thread_2()
{
    while(!sync1.load(std::memory_order_acquire));
    sync2.store(std::memory_order_release);
}

void thread_3()
{
    while(!sync2.load(std::memory_order_acquire));
    assert(data[0].load(std::memory_order_relaxed)==42);
    assert(data[1].load(std::memory_order_relaxed)==97);
    assert(data[2].load(std::memory_order_relaxed)==17);
    assert(data[3].load(std::memory_order_relaxed)==-141);
    // assert(data[4].load(std::memory_order_relaxed)==2004); // void thread_3(): Assertion `data[4].load(std::memory_order_relaxed)==2004' failed.
    assert(data[4].load(std::memory_order_relaxed)==2003);
    std::cout << "assert successfully. \n";
}

int main()
{
    std::thread t1(thread_1);
    std::thread t2(thread_2);
    std::thread t3(thread_3);
    t1.join();
    t2.join();
    t3.join();
}